<template>
	<view>
		<view class="app-components-goods-layout-form" :class="[disabled && 'is-disabled']" :style="[{ backgroundColor: bgColor || info.backColour }]">
			<load-image :src="info.backUrl" size="100vw" mode="widthFix" v-if="headerShow && info.backUrl"></load-image>
			<view class="app-components-goods-layout-form__body e-p-20">
				<view class="e-radius-20 overflow-hidd e-m-b-20 bg-white" v-if="headerShow">
					<view class="app-components-goods-layout-form__body-header" :style="[{ backgroundColor: info.color }]"></view>
					<view class="app-components-goods-layout-form__body-name e-p-30">
						<view class="e-m-b-20">
							<rich-text :nodes="info.titleText"></rich-text>
						</view>
						<rich-text :nodes="info.illustrate"></rich-text>
					</view>
				</view>

				<view class="e-m-b-20 bg-white" v-for="item in formOptions" :key="item.id">
					<view class="app-components-goods-layout-form__body-title e-p-30 e-rela" v-if="item.type === 11">
						<view class="e-m-b-20">
							<rich-text :nodes="item.title"></rich-text>
						</view>
						<rich-text :nodes="item.illustrate"></rich-text>
					</view>
					<view class="app-components-goods-layout-form__body-item" v-else>
						<Simple :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-if="item.type === 1" />
						<Paragraph :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 2" />
						<FormRadio :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 3" />
						<FormCheckbox :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 4" />
						<FormSelectBox :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 5" />
						<!-- #ifdef H5 -->
						<FileUpload :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 6" />
						<!-- #endif -->
						<Quantity :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 7" />
						<Price :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 8" />
						<DatePicker :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 9" />
						<TimePicker :info="item" :index="indexMap[item.id]" :unitEn="global.unitEn" v-model:value="list" v-else-if="item.type === 10" />
					</view>
				</view>
			</view>
		</view>
		<slot name="totalPrice" :totalPrice="newTotalPrice || totalPrice"></slot>
	</view>
</template>

<script lang="ts">
	import { defineComponent } from 'vue';
	export default defineComponent({
		name: 'goods-layout-form',
		// #ifdef MP-WEIXIN
		// 将自定义节点设置成虚拟的，更加接近Vue组件的表现，能更好的使用e-flex属性
		options: {
			virtualHost: true
		}
		// #endif
	})
</script>

<script setup lang="ts">
import { ref, reactive, computed, watch, nextTick, toRef, provide, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useCurrentInstance } from '@/common/hooks';
import type { GoodsForm } from '@/common/request/api';
import type { LayoutFormModelValue, GoodsFormContentPrice, GoodsFormOption, LayoutFormModelValueItem, GoodsFormRadioOption } from '@/typings';
import { goodsLayoutFormContextKey } from './tokens';
import type { GoodsLayoutContext, GoodsLayoutFormItemContext } from './tokens';
import { postSubmitLayoutForm } from '@/common/request/api';
import Simple from './child/simple';
import Paragraph from './child/paragraph';
import FormRadio from './child/radio';
import FormCheckbox from './child/checkbox';
import FormSelectBox from './child/selectBox';
import FileUpload from './child/fileUpload';
import Quantity from './child/quantity';
import Price from './child/price';
import DatePicker from './child/datePicker';
import TimePicker from './child/timePicker';
import { useUser, useGlobal } from '@/common/stores';
import { storeToRefs } from 'pinia';
import { empty, accMul, accAdd, isString, throttle, testNumber, priceFormat } from '@/common/utils';

const props = withDefaults(
	defineProps<{
		value: LayoutFormModelValue;
		info: GoodsForm;
		headerShow?: boolean; //显示头部的图片与表单标题
		bgColor?: string;
		price: number;
		number: number;
		skuId?: string;
		proId?: string;
		disabled?: boolean;
	}>(),
	{
		value: () => [],
		headerShow: true
	}
);

const emit = defineEmits(['update:value','priceChange']);

const ctx = useCurrentInstance();

const { t } = useI18n();

const { global } = storeToRefs(useGlobal());
const { isLogin } = storeToRefs(useUser());

const list = computed({
	get() {
		return props.value;
	},
	set(v) {
		emit('update:value', v);
	}
});

type ShowRulesItem = {
	formItem: GoodsFormOption;
	disabled: boolean;
	type?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; // 1等于 2不等于 3包含 4不包含 5大于 6大于或等于 7小于 8小于或等于 9早于 10晚于 11为空 12不为空
	value: string; //简答、段落组件判断值
	radioValue: string[]; //单选组件判断值
	checkboxValue: string[]; //多选组件判断值
	selectValue: string[]; //下拉选择组件判断值
	quantityValue: string; //数量组件判断值
	priceValue: string; //价格组件判断值
	dateValue: string; //日期组件判断值
	timeValue: string; //时间组件判断值
};

const getAnswerHasEmpty = (answer: LayoutFormModelValueItem, item: GoodsFormOption) => {
	if (item.type === 3) {
		return !!answer.formOptionsInfoId;
	} else if ([4, 5].includes(item.type)) {
		return !!answer.formOptionsInfoId?.length;
	} else if (item.type === 6) {
		return !!answer.fileList?.length;
	}
	return !empty(answer.vle);
};
const formOptions = ref<GoodsFormOption[]>([]);

watch(
	() => [props.info.formOptions, list.value],
	() => {
		formOptions.value = props.info.formOptions.filter((item,index) => {
			if (item.isHidd !== 1) {
				const showRules: ShowRulesItem[] = JSON.parse(item.displayRules || '[]');
				
				if (showRules.length&&index !== 0) {
					return showRules.some((rule) => {
						if (!rule.type) {
							return true;
						}
						let ruleType = rule.type;
						const answer = list.value.find((items) => items.vleid === `${rule.formItem.id}`);
						if (answer) {
							// 为空、不为空两种情况的判断所有题目类型都包含，但文件上传只包含这两种情况
							// 判断答案是否为空
							if (ruleType === 11) {
								return !getAnswerHasEmpty(answer, rule.formItem);
							}
							// 判断答案是否不为空
							if (ruleType === 12) {
								return getAnswerHasEmpty(answer, rule.formItem);
							}
							let status = false;
							// 简答、段落、单选、多选、下拉只存在等于、不等于 、包含、不包含、为空、不为空这六种情况的判断
							if ([1, 2].includes(rule.formItem.type)) {
								if ([1, 2].includes(ruleType)) {
									// 等于、不等于
									status = answer.vle === rule.value;
								} else if ([3, 4].includes(ruleType)) {
									// 包含、不包含
									status = !!answer.vle?.includes(rule.value);
								}
							} else if (rule.formItem.type === 3) {
								const formItem = props.info.formOptions.find((items) => items.vleid === rule.formItem.id) as GoodsFormRadioOption | undefined;
								// 判断单选题的等于、不等于 、包含、不包含这四种情况
								// 单选题的判断条件为等于、不等于时，判断答案列表长度只会为1，故单选题的等于与包含等效，不等于与不包含等效
								const formOptionsInfoId = formItem?.formOptionsInfo.find((items) => `${items.id}` === `${answer.formOptionsInfoId}`)?.vleid;
								if (formOptionsInfoId) {
									status = rule.radioValue.includes(formOptionsInfoId);
								} else {
									return false;
								}
							} else if ([4, 5].includes(rule.formItem.type)) {
								// 判断多选、下拉的等于、不等于 、包含、不包含四种情况
								let values: string[] = [];
								if (rule.formItem.type === 4) {
									values = rule.checkboxValue;
								} else if (rule.formItem.type === 5) {
									values = rule.selectValue;
								}
								const formItem = props.info.formOptions.find((items) => items.vleid === rule.formItem.id) as GoodsFormRadioOption | undefined;
								const formOptionsInfoId =
									formItem?.formOptionsInfo.filter((items) => `${items.id}` === `${answer.formOptionsInfoId}`)?.map((items) => items.vleid) || [];
								if ([1, 2].includes(ruleType)) {
									// 等于、不等于
									// 答案列表与判断条件列表相等，即答案长度与判断条件长度相等，且判断条件列表中的答案均在答案列表中时，此时状态为等于
									// values.filter(value => answer.formOptionsInfoId.includes(value)).length === values.length
									status = values.length === formOptionsInfoId.length && !values.some((value) => !formOptionsInfoId.includes(value));
								} else if ([3, 4].includes(ruleType)) {
									// 包含、不包含
									// 答案列表是否包含了判断答案列表中的任意一个
									status = values.some((value) => formOptionsInfoId.includes(value));
								}
							} else {
								// 剩余数量、自定义价格、日期、时间四种问题类型
								if (empty(answer.vle)) {
									// 空值直接判断为false
									return false;
								}
								let _ruleValue = rule.value;
								if (rule.formItem.type === 7) {
									_ruleValue = rule.quantityValue;
								} else if (rule.formItem.type === 8) {
									_ruleValue = rule.priceValue;
								} else if (rule.formItem.type === 9) {
									_ruleValue = rule.dateValue;
								} else {
									_ruleValue = `2024/01/01 ${rule.timeValue}`;
								}

								// 等于、不等于是通用的，直接判断
								if ([1, 2].includes(ruleType)) {
									// 等于、不等于
									status = answer.vle === _ruleValue;
								}

								let value = 0;
								let ruleValue = 0;
								if ([7, 8].includes(rule.formItem.type)) {
									// 判断数量、自定义价格的大于、大于或等于、小于、小于或等于这四种情况
									value = Number(answer.vle);
									ruleValue = Number(_ruleValue);
									if ([5, 7].includes(ruleType)) {
										// 大于、小于
										status = value > ruleValue;
									} else if (ruleType === 6) {
										// 大于或等于
										status = value >= ruleValue;
									} else if (ruleType === 8) {
										// 小于或等于
										status = value <= ruleValue;
									}
								} else if ([9, 10].includes(rule.formItem.type)) {
									if (rule.formItem.type === 10) {
										// 判断日期、时间的早于、晚于这两种情况
										value = new Date(`2024/01/01 ${answer.vle}`).getTime();
									} else {
										// 判断日期、时间的早于、晚于这两种情况
										value = new Date(answer.vle as string).getTime();
									}
									
									ruleValue = new Date(_ruleValue as string).getTime();
									// 等于
									if (ruleType === 1) {
										status = value === ruleValue;
									}
								}
								// 大于、小于等效于早于、晚于
								if ([5, 7, 9, 10].includes(ruleType)) {
									// 小于、早于
									if ([7, 9].includes(ruleType)) {
										// 在小于、早于的判断下，允许两个值相等，因为会在下文进行取反
										// 等同于判断非大于，早于、等于这三种情况
										status = value >= ruleValue;
									}else{
										// // 大于、晚于
										status = value > ruleValue;
									}
									
								}
							}

							// 不等于、不包含、小于、早于则取反
							if ([2, 4, 7, 9].includes(ruleType)) {
								return !status;
							}
							// 剩下则为等于、包含、大于、大于或等于、小于或等于、晚于的情况，返回status即可
							return status;
						}
						return false;
					});
				}
				return true;
			}
			return false;
		});
		
	},
	{
		deep: true,
		immediate: true
	}
);
watch(formOptions, (v) => {
	list.value.forEach((item) => {
		if (v.findIndex((option) => option.id === item.formOptionsId) === -1) {
			item.vle = '';
			item.formOptionsInfoId = '';
			item.fileList = undefined;
		}
	});
});
const indexMap = computed(() => {
	const map: Record<string, number> = {};
	if (props.info.isHiddNumber === 0) {
		let index = 1;
		formOptions.value.forEach((item) => {
			if (item.type !== 11) {
				map[item.id] = index;
				index++;
			}
		});
	}
	return map;
});

const children: GoodsLayoutFormItemContext[] = reactive([]);
const addField: GoodsLayoutContext['addField'] = (field) => {
	children.push(field);
	const item = props.value.find((item) => item.formOptionsId === field.info.id);
	if (!item) {
		list.value.push({
			formOptionsId: field.info.id,
			formOptionsInfoId: [4, 5].includes(field.info.type) ? [] : '',
			vle: '',
			formOptionsInfoIdText: '',
			vleid: field.info.vleid,
			fileList: field.info.type === 6 ? [] : undefined
		});
	}
};

const removeField: GoodsLayoutContext['removeField'] = (field) => {
	let index = children.indexOf(field);
	if (index > -1) {
		children.splice(index, 1);
	}
};

const getPrice = (item: GoodsFormContentPrice, quantity: string | number = 1) => {
	if (item.billingVle) {
		// 如果是type=7的数量组件，则会再乘以数量组件填写的值
		const billingVle = accMul(item.billingVle, quantity);
		if (item.billingMethod === 1) {
			// 固额
			return billingVle;
		} else if (item.billingMethod === 2) {
			// 百分比
			return accMul(billingVle, accMul(props.price, props.number));
		} else if (item.billingMethod === 3) {
			// 数量
			return accMul(billingVle, props.number);
		}
	}
	return 0;
};

const hasNegative = computed(() =>
	children.some((item) => {
		if (item.info.type === 8) {
			const answer = list.value.find((items) => items.formOptionsId === item.info.id);
			return Number(answer?.vle || 0) < 0;
		}
		return false;
	})
);
const newTotalPrice = ref(0);
const totalPrice = computed(() => {
	let total = 0;
	children.forEach((item) => {
		if (item.info.type === 8) {
			const answer = list.value.find((items) => items.formOptionsId === item.info.id);
			if (answer && answer.vle) {
				total = accAdd(total, answer.vle || 0);
			}
		} else {
			total = accAdd(total, item.totalPrice || 0);
		}
	});
	newTotalPrice.value = 0;
	if (total < 0) {
		total = 0;
	}
	return total;
});

watch(() => [newTotalPrice.value,totalPrice.value],() => {
	emit('priceChange',newTotalPrice.value||totalPrice.value)
},{
	immediate:true
})

const handleSubmitLayoutForm = async (isLoading = true, subType: 0 | 1 | 2 = 0) => {
	return await postSubmitLayoutForm(
		{
			proId: props.proId || '',
			subType,
			skuId: props.skuId || '',
			formId: props.info.id,
			proPrice: props.price,
			proSize: props.number,
			list: list.value.map((item) => {
				let formOptionsInfoId = isString(item.formOptionsInfoId) ? item.formOptionsInfoId : item.formOptionsInfoId?.join?.(',') || '';

				return {
					formOptionsId: item.formOptionsId,
					formOptionsInfoIdText: item.formOptionsInfoIdText,
					formOptionsInfoId,
					vle: (item.fileList?.length ? JSON.stringify(item.fileList) : item.vle) || formOptionsInfoId
				};
			})
		},
		isLoading
	);
};

watch(
	totalPrice,
	throttle(function () {
		if (isLogin.value && !props.disabled) {
			handleSubmitLayoutForm(false).then((res) => {
				newTotalPrice.value = res.data.subFormDto.totalPrice;
			});
		}
	}, 2000)
);

// 校验全部数据
const validate = (post = false): Promise<boolean> => {
	return new Promise((resolve, reject) => {
		// $nextTick是必须的，否则model的变更，可能会延后于validate方法
		nextTick(async () => {
			// 获取所有form-item的prop，交给validateField方法进行校验
			let hasSuccess = true;
			children.forEach((item) => {
				// 先校验是否必填，必填则必须有值
				if (item.info.isHidd !== 1 && 'hasRequired' in item.info && item.info?.hasRequired === 0) {
					if (item.info.type === 6) {
						if (!item.form.fileList?.length) {
							item.message = t('layoutForm.fileError');
							hasSuccess = false;
							return;
						}
					} else {
						if (item.info.type === 3) {
							if (!item.form.formOptionsInfoId) {
								item.message = t('common.请选择');
								hasSuccess = false;
								return;
							}
						} else if ([4, 5].includes(item.info.type)) {
							if (!item.form.formOptionsInfoId?.length) {
								item.message = t('common.请选择');
								hasSuccess = false;
								return;
							}
						} else if (!item.form.vle) {
							item.message = t('layoutForm.requiredError');
							hasSuccess = false;
							return;
						}
					}
				}
				const value = item.form.vle || '';
				if (value) {
					// 非必填但有值，也需校验值是否符合要求
					// 简短、段落
					if (item.info.type === 1 || item.info.type === 2) {
						if (item.info.replyRestrictions) {
							let hasError = false;

							switch (item.info.replyRestrictions) {
								case 1:
									// 只允许填写汉字
									if (/[0-9a-zA-Z]/.test(value)) {
										hasError = true;
									}
									break;
								case 2:
									// 只允许填写汉字与字母
									if (/[0-9]/.test(value)) {
										hasError = true;
									}
									break;
								case 3:
									// 只允许填写数字
									if (!testNumber(value)) {
										hasError = true;
									}
									break;
								case 4:
									// 不允许填写汉字
									if (!/[0-9a-zA-Z]/.test(value)) {
										hasError = true;
									}
									break;
								case 5:
									// 校验邮箱后缀
									const emailFormat = item.info.emailFormat?.split?.(',') || [];
									if (emailFormat.length) {
										if (!emailFormat.some((txt) => value.endsWith(txt))) {
											hasError = true;
										}
									} else {
										hasError = !new RegExp(/\S+@\S+/).test(value);
									}

									break;
							}
							if (hasError) {
								item.message = item.info.replyErrorTipText || '';
								hasSuccess = false;
								return;
							}
						}
						if (hasSuccess) {
							// 校验是否不满足长度限制
							if ((item.info.replyLimitMax && value.length > item.info.replyLimitMax) || (item.info.replyLimitMin && value.length < item.info.replyLimitMin)) {
								item.message = item.info.replyLimitErrorTipText || '';
								hasSuccess = false;
								return;
							}
						}
					}
					// 数量与价格校验是否超过最大最小值
					if (item.info.type === 7 || item.info.type === 8) {
						const [minNumber, maxNumber] = item.info.attributeValue?.split?.(',') || [];
						if (Number(value) > Number(maxNumber) || Number(value) < Number(minNumber)) {
							item.message = item.info.billingErrorTipText || '';
							hasSuccess = false;
						}
					}
				}
			});
			if (!hasSuccess) {
				ctx.$toastApp(t('layoutForm.validateError'));
			}

			if (post) {
				try {
					await handleSubmitLayoutForm(true, 2);
				} catch (error) {
					reject();
				}
			}

			resolve(hasSuccess);
		});
	});
};

provide(
	goodsLayoutFormContextKey,
	reactive({
		addField,
		removeField,
		disabled: toRef(props, 'disabled'),
		getPrice
	})
);

onUnmounted(() => {
	children.splice(0);
});

defineExpose({
	validate
});
</script>

<style lang="scss" scoped>
@include appBem_b(components-goods-layout-form) {
	@include appWhen(disabled) {
		position: relative;

		&::after {
			@include pseudoClass;
			top: 0;
			right: 0;
			bottom: 0;
			left: 0;
			z-index: 2;
		}
	}
	@include appBem_e(body) {
		&-header {
			height: 16rpx;
		}
		&-title {
			&::after {
				@include pseudoClass;
				background-color: var(--color-primary);
				width: 8rpx;
				height: 28rpx;
				top: 38rpx;
				left: 0;
			}
		}
	}
}
</style>
